/*************************************************************
* @file    	clock.c
* @author 	dralee
* @version	v1.0
* @date			2021-12-01
* @brief		配置系统时钟
*	使用HSE时，设置系统时钟步骤：
*	  1、开启HSE并等待HSE稳定
*   2、设置AHB、APB2、APB1预分频因子
*		3、设置PLL时钟来源和PLL倍频因子，设置各种频率主要就在这
*		4、开启PLL并等待PLL稳定
*		5、把PLLCLK切换为系统时钟SYSCLK
*		6、读取时钟切换状态位，确保PLLCLK被选为系统时钟
*************************************************************/
#include "clock.h"
#include "stm32f10x_rcc.h"

/*****************************************************************************
* @brief 使用HSE设置系统时钟：SYSCLK，AHB总线时钟：HCLK，
*		APB2总线时钟：PCLK2，APB1总线时钟：PCLK1
*		PCLK2 = HCLK = SYSCLK
*		PCLK1 = HCLK/2，最高只能36M
* @param pllmul是PLL倍频因子，使用时RCC_PLLMul_x,x:[2,3...16]
*	如：HSE_SetSysClock(RCC_PLLMul_9)，则系统时钟为：8MHz*9=72MHz
*			HSE_SetSysClock(RCC_PLLMul_16)，则系统时钟为：8MHz*16=128MHz，超频慎用
* @retval 无
*****************************************************************************/
void HSE_SetSysClock(uint32_t pllmul)
{
	__IO uint32_t HSEStartUpStatus = 0;
	
	// 把RCC外设初始化为复位状态
	RCC_DeInit();
	
	// 使能HSE，开启外部晶振，一般是8MHz
	RCC_HSEConfig(RCC_HSE_ON);
	
	// 等待HSE启动稳定
	HSEStartUpStatus = RCC_WaitForHSEStartUp();
	
	// 只有HSE稳定后才继续执行
	if(HSEStartUpStatus == SUCCESS)
	{
		// 操作FLASH闪存，如不操作FLASH，以下两个可注释
		// 使能FLASH预存取缓冲空
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
		
		// SYSCLK周期与闪存访问时间比例设置，统一成2（SYSCLK低于48M也可工作，如0或1时，
		// 配置的SYSCLK超出范围，则会进入硬件错误，程序死
		// 0:  0 < SYSCLK <= 24M
		// 1: 24 < SYSCLK <= 48M
		// 2: 48 < SYSCLK <= 72M
		FLASH_SetLatency(FLASH_Latency_2);
		
		
		// AHB预分频因子设置1分频，HCLK = SYSCLK
		RCC_HCLKConfig(RCC_SYSCLK_Div1);
		
		// APB2预分频因子设置1分频，PCLK2 = HCLK
		RCC_PCLK2Config(RCC_HCLK_Div1);
		
		// APB1预分频因子设置2分频，PCLK1 = HCLK/2
		RCC_PCLK1Config(RCC_HCLK_Div2);
		
		// 设置各种频率主要在此，设置PLL时钟来源于HSE，设置PLL倍频因子: PLLCLK = 8MHz * pllmul
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1, pllmul);
		
		// 开启PLL
		RCC_PLLCmd(ENABLE);
		
		// 等待PLL稳定
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
		{
		}
		
		// 当PLL稳定后，把PLL时钟切换为系统时钟SYSCLK
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
		
		// 读取时钟切换状态，确保PLLCLK被选为系统时钟
		while(RCC_GetSYSCLKSource() != 0x08)
		{
		}
	}
	else
	{
		// 如果HSE开启失败，用户可在此添加出错代码处理
		// 当HSE开启失败或故障时，单片机会自动把HSI设置为系统时钟，HSI内部高速时钟8MHz
		while(1)
		{
		}
	}
}

/*****************************************************************************
* @brief 使用HSI设置系统时钟：SYSCLK，AHB总线时钟：HCLK，
*		APB2总线时钟：PCLK2，APB1总线时钟：PCLK1
*		PCLK2 = HCLK = SYSCLK
*		PCLK1 = HCLK/2，最高只能36M
* @param pllmul是PLL倍频因子，使用时RCC_PLLMul_x,x:[2,3...16]
*	如：HSI_SetSysClock(RCC_PLLMul_9)，则系统时钟为：4MHz*9=36MHz
*			HSI_SetSysClock(RCC_PLLMul_16)，则系统时钟为：4MHz*16=64MHz
*	HSI作为时钟来源，经过PLL倍频作为系统时钟，这是HSE故障时才使用的方法
* HSI会因温度等原因会有漂移，不稳定，一般不会用HSI作为时钟来源，除非迫不得已
* 如HSI作为PLL时钟来源，必须二分频后才可以，即HSI/2，而PLL倍频因子最大为16
*	所以当使用HSI时，SYSCLK最大只能是4M*16=64M
* @retval 无
*****************************************************************************/
void HSI_SetSysClock(uint32_t pllmul)
{
	__IO uint32_t HSIStartUpStatus = 0;
	
	// 把RCC外设初始化为复位状态
	RCC_DeInit();
	
	// 使能HSI
	RCC_HSICmd(ENABLE);
	
	// 等待HSI就绪
	HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY;
	
	// 只有HSI稳定后才继续执行
	if(HSIStartUpStatus == RCC_CR_HSIRDY)
	{
		// 操作FLASH闪存，如不操作FLASH，以下两个可注释
		// 使能FLASH预存取缓冲空
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
		
		// SYSCLK周期与闪存访问时间比例设置，统一成2（SYSCLK低于48M也可工作，如0或1时，
		// 配置的SYSCLK超出范围，则会进入硬件错误，程序死
		// 0:  0 < SYSCLK <= 24M
		// 1: 24 < SYSCLK <= 48M
		// 2: 48 < SYSCLK <= 72M
		FLASH_SetLatency(FLASH_Latency_2);
		
		
		// AHB预分频因子设置1分频，HCLK = SYSCLK
		RCC_HCLKConfig(RCC_SYSCLK_Div1);
		
		// APB2预分频因子设置1分频，PCLK2 = HCLK
		RCC_PCLK2Config(RCC_HCLK_Div1);
		
		// APB1预分频因子设置2分频，PCLK1 = HCLK/2
		RCC_PCLK1Config(RCC_HCLK_Div2);
		
		// 设置各种频率主要在此，设置PLL时钟来源于HSE，设置PLL倍频因子: PLLCLK = 8MHz * pllmul
		RCC_PLLConfig(RCC_PLLSource_HSI_Div2, pllmul);
		
		// 开启PLL
		RCC_PLLCmd(ENABLE);
		
		// 等待PLL稳定
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
		{
		}
		
		// 当PLL稳定后，把PLL时钟切换为系统时钟SYSCLK
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
		
		// 读取时钟切换状态，确保PLLCLK被选为系统时钟
		while(RCC_GetSYSCLKSource() != 0x08)
		{
		}
	}
	else
	{
		// 如果HSI开启失败，用户可在此添加出错代码处理
		// 当HSE开启失败或故障时，单片机会自动把HSI设置为系统时钟，HSI内部高速时钟8MHz
		while(1)
		{
		}
	}	
}
